#ifndef TYPETREE_H
#define TYPETREE_H

#include <list>
#include <vector>
#include <string>

#include "Runtime/Misc/Allocator.h"
#include "Configuration/UnityConfigure.h"
#include "SerializationMetaFlags.h"
#include "Runtime/Allocator/MemoryMacros.h"
#include "Runtime/Serialize/SwapEndianBytes.h"

typedef UNITY_VECTOR(kMemSerialization, UInt8) SerializationCache;

template<bool kSwap, class T>
// Need this in order for Asset bundles to load correctly when the code is optimized
#if UNITY_BB10
__attribute__ ((noinline))
#endif
void ReadHeaderCache (T& t, UInt8 const*& c)
{
	t = *(T const*)c;
	if (kSwap)
		SwapEndianBytes (t);
	c += sizeof (T);
}

template<bool kSwap, class T>
void WriteHeaderCache (const T& t, SerializationCache& vec)
{
	vec.resize (vec.size () + sizeof (T));
	T& dst = *reinterpret_cast<T*> (&vec[vec.size () - sizeof (T)]);
	dst = t;
	if (kSwap)
		SwapEndianBytes (dst);
}

UNITY_STR_IMPL(TypeTreeString, kMemTypeTree);

/// A TypeTree contains information on serialized data.
/// It is a tree storing the name, and type and other information generated by the serialization code,
/// of every variable and all its variables that it might contain.
/// A TypeTree can be generated using a ProxyTransfer class.
/// There are convenience function which generate them from an object in TransferUtility.h

class TypeTree
{
	public:

	typedef UNITY_LIST(kMemTypeTree,TypeTree) TypeTreeList;
	typedef TypeTreeList::iterator iterator;
	typedef TypeTreeList::const_iterator const_iterator;
	TypeTreeList                m_Children; // The children of the Type (eg. a Vector3f has 3 children, float x, float y, float z)
	TypeTree*					m_Father;
	
	TypeTreeString				m_Type;// The type of the variable (eg. "Vector3f", "int")
	TypeTreeString				m_Name;// The name of the property (eg. "m_LocalPosition")
	SInt32						m_ByteSize;//= -1 if its not determinable (arrays) 
	SInt32						m_Index; // The index of the property (Prefabs use this index in the override bitset)
	SInt32						m_IsArray;// Is the TypeTree an array (first child is the size, second child is the type of the array elements)
	SInt32						m_Version; // The version of the serialization format as represented by this type tree.  Usually determined by Transfer() functions.

	// Serialization meta data (eg. to hide variables in the property editor)
	// Children or their meta flags with their parents!
	TransferMetaFlags			m_MetaFlag;
	SInt32						m_ByteOffset; // The byteoffset into the property in memory. When a variable on the stack is serialized m_ByteOffset is -1.
	void*						m_DirectPtr; // The direct ptr into the property in memory. NULL if it can't be used.
	
	TypeTree ();
	TypeTree (const std::string& name, const std::string& type, SInt32 size);
	
	void DebugPrint (std::string& buffer, int level = 0) const;
	bool IsBasicDataType ()const { return m_Children.empty () && m_ByteSize > 0; }
	
	iterator begin ()			{ return m_Children.begin (); }
	iterator end () 			{ return m_Children.end (); }
	const_iterator begin ()	const		{ return m_Children.begin (); }
	const_iterator end () const 		{ return m_Children.end (); }
	
	void operator = (const TypeTree& typeTree);
};

void GetTypePath (const TypeTree* type, std::string& s);
const TypeTree& GetElementTypeFromContainer (const TypeTree& typeTree);
SInt32 GetContainerArraySize (const TypeTree& typeTree, void* data);
void AppendTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end);
void AppendTypeTree (TypeTree& typeTree, const TypeTree& typeTreeToAdd);
void RemoveFromTypeTree (TypeTree& typeTree, TypeTree::iterator begin, TypeTree::iterator end);

class Type
{
	TypeTree*	m_OldType;	// Type loaded from Disk
	TypeTree*	m_NewType;	// Active type
	bool			m_Equals;	// Are oldType and newType equal
	
	public:
	
	Type ();
	
	~Type ();
	
	TypeTree* GetOldType () 		{ return m_OldType; }
	TypeTree* GetNewType () 		{ return m_NewType; }

	bool EqualTypes () 				{ return m_Equals; }
	
	void SetOldType (TypeTree* t);
	void SetNewType (TypeTree* t);	
};

/// Reads/Writes a typetree to a vector<UInt8> with the first four bits being the version of the typetree
bool ReadVersionedTypeTreeFromVector (TypeTree* typeTree, UInt8 const*& iterator, UInt8 const* end, bool swapEndianess);

/// Reads/Writes a typeTree to a cache, used direcly by SerializedFile
bool ReadTypeTree (TypeTree& t, UInt8 const*& iterator, UInt8 const* end, int version, bool swapEndian);
void WriteTypeTree (TypeTree& t, SerializationCache& cache, bool swapEndianess);

bool IsStreamedBinaryCompatbile (const TypeTree& lhs, const TypeTree& rhs);
bool IsStreamedBinaryCompatbileAndIndices (const TypeTree& lhs, const TypeTree& rhs);


void WriteString (TypeTreeString const& s, SerializationCache& cache);
void WriteString (UnityStr const& s, SerializationCache& cache);
bool ReadString (TypeTreeString& s, UInt8 const*& iterator, UInt8 const* end);
bool ReadString (UnityStr& s, UInt8 const*& iterator, UInt8 const* end);

UInt32 HashTypeTree (TypeTree& typeTree);

#endif
